/*----------------------------------------------------------------------------
 * Copyright (c) <2013-2015>, <Huawei Technologies Co., Ltd>
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 * of conditions and the following disclaimer in the documentation and/or other materials
 * provided with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 * to endorse or promote products derived from this software without specific prior written
 * permission.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *---------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
 * Notice of Export Control Law
 * ===============================================
 * Huawei LiteOS may be subject to applicable export control laws and regulations, which might
 * include those applicable to Huawei LiteOS of U.S. and the country in which you are located.
 * Import, export and usage of Huawei LiteOS in any manner by you shall be in compliance with such
 * applicable export control laws and regulations.
 *---------------------------------------------------------------------------*/

#include "usb_init.h"

typedef struct usb_info{
    bool bInit;
    controller_type ctype;
    device_type dtype;
}usb_info_t;

static char *devname = NULL;
static bool uvc_enable = false;
static usb_info_t usbInfo = {false, (controller_type)0xFFFF, (device_type)0xFFFF};
static struct mtx usb_mtx = PTHREAD_MUTEX_INITIALIZER;

extern const char* fetach_usbversion(void);

/* extern struct module_data bsd_##name##_##busname##_driver_mod */
#ifdef LOSCFG_DRIVERS_USB_HOST_EHCI
/* host or device controller modules */
/* usb controller modules  link xxx-controller bus*/
extern struct module_data bsd_usbus_hi_ehci_driver_mod;
#endif

#if defined (LOSCFG_DRIVERS_USB_HOST_XHCI)
extern struct module_data bsd_usbus_hi_xhci_driver_mod;
#endif

#ifdef LOSCFG_DRIVERS_USB_HOST_DRIVER
/* root hub driver modules  link to usb bus*/
extern struct module_data bsd_uhub_usbus_driver_mod;
/* xxx class driver moudles  link to usb hub bus*/
extern struct module_data bsd_uhub_uhub_driver_mod;
#endif

#ifdef LOSCFG_DRIVERS_USB_4G_MODEM
extern struct module_data bsd_cdce_uhub_driver_mod;
#endif

#if defined (LOSCFG_DRIVERS_USB_SERIAL) || defined (LOSCFG_DRIVERS_USB_4G_MODEM)
extern struct module_data bsd_u3g_uhub_driver_mod;
#endif

#ifdef LOSCFG_DRIVERS_USB_ETHERNET
extern struct module_data bsd_axe_uhub_driver_mod;
extern struct module_data bsd_axge_uhub_driver_mod;
#endif

#ifdef LOSCFG_DRIVERS_USB_RNDIS_HOST
extern struct module_data bsd_urndis_uhub_driver_mod;
#endif

#ifdef LOSCFG_DRIVERS_USB_MASS_STORAGE
extern struct module_data bsd_umass_uhub_driver_mod;
#endif

#ifdef LOSCFG_DRIVERS_USB_MASS_STORAGE_GADGET
extern struct module_data bsd_fmass_simple_driver_mod;
#endif

#ifdef LOSCFG_DRIVERS_USB_COMPOSITE_GADGET
extern struct module_data bsd_composite_simple_driver_mod;
#endif

#ifdef LOSCFG_DRIVERS_USB_UVC_GADGET
extern struct module_data bsd_fuvc_simple_driver_mod;
#endif

#ifdef LOSCFG_DRIVERS_USB_UAC_GADGET
extern struct module_data bsd_fuac_simple_driver_mod;
#endif

#ifdef LOSCFG_DRIVERS_USB_CAMERA_GADGET
extern struct module_data bsd_fcamera_simple_driver_mod;
#endif

#ifdef LOSCFG_DRIVERS_USB_DFU_GADGET
extern struct module_data bsd_composite_simple_driver_mod;
#endif

#ifdef LOSCFG_DRIVERS_USB_SERIAL_GADGET
extern struct module_data bsd_fserial_simple_driver_mod;
#endif

#ifdef LOSCFG_DRIVERS_USB_ETHERNET_GADGET
extern struct module_data bsd_fethernet_simple_driver_mod;
#endif

#ifdef LOSCFG_DRIVERS_USB_ETH_SER_GADGET
extern struct module_data bsd_fmulti_simple_driver_mod;
#endif

#ifdef LOSCFG_DRIVERS_USB_DFU_GADGET
extern struct module_data bsd_fdfu_simple_driver_mod;
#endif

#ifdef LOSCFG_DRIVERS_USB_WIRELESS
extern struct module_data bsd_usb_linux_uhub_driver_mod;
#endif

static struct module_data* bsd_driver_mode_list[] = {
#ifdef LOSCFG_DRIVERS_USB_HOST_EHCI
/* xxx controller modules */
    /* usb generial controller modules */
    &bsd_usbus_hi_ehci_driver_mod,
#endif

#if defined (LOSCFG_DRIVERS_USB_HOST_XHCI)
    &bsd_usbus_hi_xhci_driver_mod,
#endif


#ifdef LOSCFG_DRIVERS_USB_HOST_DRIVER
    /* xxx driver modules */
    &bsd_uhub_uhub_driver_mod,
    &bsd_uhub_usbus_driver_mod,
#endif

#ifdef LOSCFG_DRIVERS_USB_4G_MODEM
    &bsd_cdce_uhub_driver_mod,
    &bsd_u3g_uhub_driver_mod,
#endif

#if defined (LOSCFG_DRIVERS_USB_SERIAL) || defined (LOSCFG_DRIVERS_USB_4G_MODEM)
    &bsd_u3g_uhub_driver_mod,
#endif

#ifdef LOSCFG_DRIVERS_USB_MASS_STORAGE
    &bsd_umass_uhub_driver_mod,
#endif

#ifdef LOSCFG_DRIVERS_USB_ETHERNET
    &bsd_axe_uhub_driver_mod,
    &bsd_axge_uhub_driver_mod,
#endif

#ifdef LOSCFG_DRIVERS_USB_RNDIS_HOST
    &bsd_urndis_uhub_driver_mod,
#endif

#ifdef LOSCFG_DRIVERS_USB_MASS_STORAGE_GADGET
    &bsd_fmass_simple_driver_mod,
#endif

#ifdef LOSCFG_DRIVERS_USB_COMPOSITE_GADGET
    &bsd_composite_simple_driver_mod,
#endif

#ifdef LOSCFG_DRIVERS_USB_UVC_GADGET
    &bsd_fuvc_simple_driver_mod,
#endif

#ifdef LOSCFG_DRIVERS_USB_UAC_GADGET
    &bsd_fuac_simple_driver_mod,
#endif

#ifdef LOSCFG_DRIVERS_USB_CAMERA_GADGET
    &bsd_fcamera_simple_driver_mod,
#endif

#ifdef LOSCFG_DRIVERS_USB_SERIAL_GADGET
    &bsd_fserial_simple_driver_mod,
#endif

#ifdef LOSCFG_DRIVERS_USB_ETHERNET_GADGET
    &bsd_fethernet_simple_driver_mod,
#endif

#ifdef LOSCFG_DRIVERS_USB_ETH_SER_GADGET
    &bsd_fmulti_simple_driver_mod,
#endif

#ifdef LOSCFG_DRIVERS_USB_DFU_GADGET
    &bsd_fdfu_simple_driver_mod,
#endif

#ifdef LOSCFG_DRIVERS_USB_WIRELESS
    &bsd_usb_linux_uhub_driver_mod,
#endif

    NULL
};

#if defined (LOSCFG_DRIVERS_USB_HOST_XHCI)
    extern int hixhci_init(void);
#endif
#ifdef LOSCFG_DRIVERS_USB_HOST_EHCI
    extern int hiehci_init(void);
#endif
#ifdef LOSCFG_DRIVERS_USB2_DEVICE_CONTROLLER
    extern int hiudc_init(void);
    extern int hiudc_exit(void);
#endif
#if defined(LOSCFG_DRIVERS_USB3_DEVICE_CONTROLLER)
    extern int hiudc3_init(void);
    extern int hiudc3_exit(void);
#endif

void usbinfo_clean(void)
{
    devname = NULL;
    uvc_enable = false;
    usbInfo.ctype = (controller_type)0xFFFF;
    usbInfo.dtype = (device_type)0xFFFF;
    usbInfo.bInit = false;
    return;
}
static UINT32 devname_set(device_type dtype)
{
    UINT32 ret = LOS_OK;
    uvc_enable = false;
    switch(dtype)
    {
        case DEV_SERIAL:
            devname = "fserial";
            break;
        case DEV_ETHERNET:
            devname = "fethernet";
            break;
        case DEV_SER_ETH:
            devname = "fmulti";
            break;
        case DEV_DFU:
            devname = "fdfu";
            break;
        case DEV_MASS:
            devname = "fmass";
            break;
        case DEV_UVC:
            devname = "fuvc";
            uvc_enable = true;
            break;
        case DEV_UAC:
            devname = "fuac";
            break;
        case DEV_CAMERA:
            devname = "fcamera";
            uvc_enable = true;
            break;
        default:
            ret = LOS_NOK;
            PRINT_ERR("device type %d error\n", dtype);
            break;
    }
    return ret;
}

char *devname_get(void)
{
    return devname;
}

bool device_is_uvc(void)
{
    return uvc_enable;
}

static UINT32 usb_loadonce(void)
{
    struct module_data* data = NULL;
    unsigned int i, ret;

    dprintf("usb %s\n",fetach_usbversion());

#ifdef LOSCFG_DRIVERS_USB
    dprintf("usb %p ~+ %d KB\n", g_usb_mem_addr_start, (g_usb_mem_size >> 10));

    /* init dma memory */
    ret = LOS_MemInit((void *)g_usb_mem_addr_start, g_usb_mem_size);
    if (ret != LOS_OK) {
        dprintf("*** usb init memory error 0x%x!! ***\n\n", ret);
        goto fail;
    }

    /* init quirk */
    usb_quirk_init(NULL);

    for (i = 0; (data = bsd_driver_mode_list[i]) && (data != NULL); i++) {
        module_register(data);
    }

    devclass_module_dump();
#endif

#ifdef LOSCFG_USB_DEBUG
    extern void usb_debug_module_regsiter(void);
    usb_debug_module_regsiter();
#endif

    return 0;

fail:
#ifdef LOSCFG_MEM_MUL_POOL
    (void)LOS_MemDeInit((void *)g_usb_mem_addr_start);
#endif
    (void)memset_s((void *)g_usb_mem_addr_start, g_usb_mem_size, 0, g_usb_mem_size);

    return 1;
}

static void usb_unloadonce(void)
{
    unsigned int i;
    struct module_data* data = NULL;

#ifdef LOSCFG_USB_DEBUG
    extern void usb_debug_module_unregsiter(void);
    usb_debug_module_unregsiter();
#endif

#ifdef LOSCFG_DRIVERS_USB
    for (i = 0; (data = bsd_driver_mode_list[i]) && (data != NULL); i++) {
        module_unregister(data);
    }

#ifdef LOSCFG_MEM_MUL_POOL
    (void)LOS_MemDeInit((void *)g_usb_mem_addr_start);
#endif

    (void)memset_s((void *)g_usb_mem_addr_start, g_usb_mem_size, 0, g_usb_mem_size);
#endif

    return;
}

/*
step1: modify DRIVER_MODULE,register all driver module
step2: make ehci/ohci device (direct skip pci bus)
step3: insert ehci/ohci device into usb controller
step4: creat ehci/ohci root hub device
step5: ehci/ohci transfer setup/start
 */
UINT32 usb_init(controller_type ctype, device_type dtype)
{
    unsigned int ret = LOS_OK;
    static int usbloaded = 0;

    dprintf("\n******** %s in **********\n",__FUNCTION__);

    mtx_lock(&usb_mtx);
    if (usbInfo.bInit)
    {
        dprintf("\n duplicate usb_init %s, ctype:%d dtype:%d\n", __FUNCTION__, usbInfo.ctype, usbInfo.dtype);
        mtx_unlock(&usb_mtx);
        return LOS_NOK;
    }

    if (usbloaded == 0){
        ret = usb_loadonce();
        if (ret){
            goto err;
        }
    }

    if (ctype == HOST)
    {
#if defined (LOSCFG_DRIVERS_USB_HOST_XHCI)
        ret = hixhci_init();
#endif
#ifdef LOSCFG_DRIVERS_USB_HOST_EHCI
        ret = hiehci_init();
#endif
    }
    else if(ctype == DEVICE)
    {
        ret = devname_set(dtype);
        if (ret != LOS_OK)
        {
            PRINT_ERR("device type is not supported\n");
            goto err;
        }
#ifdef LOSCFG_DRIVERS_USB2_DEVICE_CONTROLLER
        ret = hiudc_init();
#endif
#if defined(LOSCFG_DRIVERS_USB3_DEVICE_CONTROLLER)
        ret = hiudc3_init();
#endif
    }
    else
    {
        PRINT_ERR("controller type %d is error\n", ctype, dtype);
        goto err;
    }

    if (ret != LOS_OK)
    {
        goto err;
    }

    usbInfo.bInit = true;
    usbInfo.ctype = ctype;
    usbInfo.dtype = dtype;

    usbloaded = 1;
    mtx_unlock(&usb_mtx);
    dprintf("******** %s ok**********\n\n",__FUNCTION__);
    return LOS_OK;

err:
    mtx_unlock(&usb_mtx);
    if (!usbloaded) {
        usb_unloadonce();
    }
    dprintf("******** %s fail**********\n\n",__FUNCTION__);

    return LOS_NOK;
}

UINT32 usb_deinit(void)
{
    unsigned int ret = LOS_OK;
    UINTPTR uvIntSave;
    dprintf("******** %s in **********\n\n",__FUNCTION__);

    mtx_lock(&usb_mtx);
    uvIntSave = LOS_IntLock();
    if (false == usbInfo.bInit)
    {
        dprintf("******** %s out, no init **********\n\n", __FUNCTION__);
        goto err;
    }

    if (HOST == usbInfo.ctype)
    {
        dprintf("******** %s fail, host not support **********\n\n", __FUNCTION__);
        goto err;
    }

#ifdef LOSCFG_DRIVERS_USB2_DEVICE_CONTROLLER
    ret = hiudc_exit();
#endif
#if defined (LOSCFG_DRIVERS_USB3_DEVICE_CONTROLLER)
    ret = hiudc3_exit();
#endif
    if (ret) {
        dprintf("******** %s fail, %d **********\n\n", __FUNCTION__, ret);
        goto err;
    }

    dprintf(" ** %s uninit success **\n", devname_get());
    usbinfo_clean();
    LOS_IntRestore(uvIntSave);
    mtx_unlock(&usb_mtx);
    return LOS_OK;

err:
    LOS_IntRestore(uvIntSave);
    mtx_unlock(&usb_mtx);
    return LOS_NOK;
}

bool usb_is_devicemode(void)
{
    bool bdevice = false;
    bdevice = hiusb_is_device_mode();
    return bdevice;
}

